/**
 * 
 */
package org.codesketch.flamingo.ssh;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.log4j.Logger;
import org.codesketch.flamingo.Node;
import org.codesketch.flamingo.domain.Response;

import com.trilead.ssh2.Connection;
import com.trilead.ssh2.Session;

/**
 * @author quirino
 * 
 */
public class SSHClient {
	private static final Logger LOGGER = Logger.getLogger(SSHClient.class);

	private transient Connection connection;
	private transient boolean connected = false;

	public void connect(final Node node) {
		connection = new Connection(node.getHostname(), node.getPort());
		try {
			connection.connect();
			connected = connection.authenticateWithPassword(node.getUsername(),
					node.getPassword());
		} catch (IOException e) {
			LOGGER.error(
					String.format("Unable to connect to %s using SSH",
							node.getHostname()), e);
			connected = false;
		}
	}

	public void disconnect() {
		connection.close();
	}

	/**
	 * Check if this client is connected to the remote server.
	 * 
	 * @return true if the client is connected to the remote server, false
	 *         otherwise.
	 */
	public boolean isConnected() {
		return connected;
	}

	public Response<byte[]> execute(final String command) {
		Response<byte[]> response;
		Session session = null; // NOPMD by quirino.brizi on 02/04/12 12:27
		try {
			session = connection.openSession();
			session.execCommand(command);
			int status;
			try {
				status = session.getExitStatus();
			} catch (Exception e) {
				status = 0;
			}
			
			if (status == 0) {
				final byte[] out = toByte(session.getStdout());
				// has the response been written on STDOUT?
				if(out.length>0) {
					response = new Response<byte[]>(out, status, session.getExitSignal());
				} else {
					final byte[] err = toByte(session.getStderr());
					response = new Response<byte[]>(err, status, session.getExitSignal());
				}
			} else {
				final byte[] err = toByte(session.getStderr());
				response = new Response<byte[]>(err, status, session.getExitSignal());
			}
		} catch (IOException e) {
			LOGGER.error(String.format(
					"Exception executing command {%s} on {%s}", command, connection.getHostname()), e);
			response = new Response<byte[]>();
		} finally {
			if (null != session) {
				session.close();
			}
		}
		return response;
	}

	public static byte[] toByte(final InputStream inputStream) throws IOException {
		byte[] buf;

		if (inputStream instanceof ByteArrayInputStream) {
			final int size = inputStream.available();
			buf = new byte[size];
			inputStream.read(buf, 0, size);
		} else {
			final ByteArrayOutputStream bos = new ByteArrayOutputStream();
			buf = new byte[1024];
			int len;
			do {
				len = inputStream.read(buf, 0, 1024);
				if(len == -1) {
					break;
				}
				bos.write(buf, 0, len);
			} while(len >= 0);
//			while ((len = inputStream.read(buf, 0, size)) != -1) {
//				bos.write(buf, 0, len);
//			}
			buf = bos.toByteArray();
			bos.close();
		}
		return buf;
	}

	protected byte[] expand(final byte[] src, final int expand) {
		final byte[] dst = new byte[src.length + expand];
		System.arraycopy(src, 0, dst, 0, src.length);
//		for (int i = 0; i < src.length; i++) {
//			dst[i] = src[i];
//		}
		return dst;
	}
}
